use windows_sys::{
    w,
    Win32::{
        Foundation::{
            CloseHandle, GetLastError, ERROR_ALREADY_EXISTS, HANDLE, HINSTANCE, LPARAM, LRESULT,
            POINT, WPARAM,
        },
        System::Threading::{CreateMutexA, GetCurrentThreadId},
        UI::{
            Input::KeyboardAndMouse::{GetAsyncKeyState, VK_LWIN, VK_MENU, VK_RWIN, VK_TAB},
            WindowsAndMessaging::{
                CallNextHookEx, DispatchMessageW, GetMessageW, PostThreadMessageW,
                SetWindowsHookExW, TranslateMessage, UnhookWindowsHookEx, HC_ACTION, HHOOK,
                KBDLLHOOKSTRUCT, MSG, WH_KEYBOARD_LL, WM_QUIT,
            },
        },
    },
};

use core_api::is_process_alive;
use log::{error, info};
const WM_KEYDOWN: WPARAM = 0x0100;
const WM_SYSKEYDOWN: WPARAM = 0x0104;

unsafe extern "system" fn low_level_keyboard_proc(
    n_code: i32,
    w_param: WPARAM,
    l_param: LPARAM,
) -> LRESULT {
    if n_code == HC_ACTION as i32 && (w_param == WM_KEYDOWN || w_param == WM_SYSKEYDOWN) {
        let hook_struct = *(l_param as *const KBDLLHOOKSTRUCT);
        let vk_code = hook_struct.vkCode as u16;
        // Handle keyboard events here

        // info!("low_level_keyboard_proc keycode --->{}", vk_code);

        if vk_code == VK_LWIN
        // 截获左win(开始菜单键)
        {
            //info!("LLLLLLL win");
            // 返回 1 表示拦截该消息
            return 1;
        }

        if vk_code == VK_RWIN
        // 截获右win
        {
            //info!("RRRRRRR win");
            return 1;
        }

        if vk_code == VK_TAB && (GetAsyncKeyState(VK_MENU.into()) & 0x8000u16 as i16 != 0) {
            //info!("AAAAAAlt + Tab pressed");
            return 1; // 拦截 Alt+Tab
        }
    }
    return CallNextHookEx(std::ptr::null_mut(), n_code, w_param, l_param);
}

use std::sync::{Arc, Mutex};

/// 包装 HHOOK 让它实现 Send
#[derive(Copy, Clone)]
struct SafeHHook(HHOOK);
unsafe impl Send for SafeHHook {}
unsafe impl Sync for SafeHHook {}
struct HookState {
    hook: Option<SafeHHook>, // 钩子
    thread_id: Option<u32>,  // 线程id
}
// 全局状态
static HOOK_STATE: Mutex<Option<Arc<Mutex<HookState>>>> = Mutex::new(None);
// 虽然钩子在当前窗口无效，但是在其他窗口皆有效，如果启动第二个程序也有效
// 所以需要单独做一个exe 来执行钩子
pub fn start_keyboard_hook() {
    std::thread::spawn(|| unsafe {
        let thread_id = GetCurrentThreadId();
        // 创建钩子状态
        let state = Arc::new(Mutex::new(HookState {
            hook: None,
            thread_id: Some(thread_id),
        }));

        let hook_handle = SetWindowsHookExW(
            WH_KEYBOARD_LL,
            Some(low_level_keyboard_proc),
            std::ptr::null_mut(), //这个参数也可以 是h_instance,传 0 就表示当前模块
            0,
        );
        if hook_handle == std::ptr::null_mut() {
            error!("Failed to sethook: {}", GetLastError());
            return;
        }

        // 保存钩子和状态
        let mut hook_state = HOOK_STATE.lock().unwrap();
        *hook_state = Some(state.clone());

        let mut state = state.lock().unwrap();
        state.hook = Some(SafeHHook(hook_handle));
        // 这里必须显式释放锁,否则进入消息循环后 无法释放锁
        drop(state);
        drop(hook_state);

        info!("sethook success!");
        /* 如果需要在没有窗口的程序中执行hook 则需要如下代码*/
        let mut msg: MSG = MSG {
            hwnd: std::ptr::null_mut(),
            message: 0,
            wParam: 0,
            lParam: 0,
            time: 0,
            pt: POINT { x: 0, y: 0 },
        };

        // 在Hook线程中, 无论是用的哪种钩子,都需要一个消息循环轮询当前线程中的消息队列:
        while GetMessageW(&mut msg, std::ptr::null_mut(), 0, 0) != 0 {
            TranslateMessage(&msg);
            DispatchMessageW(&msg);
        }

        // https://learn.microsoft.com/zh-cn/windows/win32/api/winuser/nf-winuser-unhookwindowshookex
        // 成功，则返回值为非零值
        // 函数失败，则返回值为零
        if UnhookWindowsHookEx(hook_handle) == 0 {
            error!("failed to unhook:{}!", GetLastError());
            return;
        } else {
            info!("unhook success!");
        }
    });
}

pub fn stop_keyboard_hook() {
    let hook_state = HOOK_STATE.lock().unwrap();

    if let Some(state) = hook_state.as_ref() {
        let mut state = state.lock().unwrap();

        // 在主线程安装钩子是不需要执行以下代码
        if let Some(thread_id) = state.thread_id.take() {
            // 发送消息后 可以使 while GetMessageW 循环退出
            let result = unsafe { PostThreadMessageW(thread_id, WM_QUIT, 0, 0) };
            if result == 0 {
                error!("send WM_QUIT error: {}!", unsafe { GetLastError() });
            } else {
                info!("send WM_QUIT success!");
            }
        }

        if let Some(_hook_handle) = state.hook.take() {
            // 这里已经通过PostThreadMessageW 发送消息结束了hook
            // 所以不需要显示释放
            // unsafe {
            //     // 释放钩子
            //     if UnhookWindowsHookEx(hook_handle.0) == 0 {
            //         eprintln!("Failed to UnhookWindowsHookEx: {}", GetLastError());
            //         return;
            //     }
            // }
        }
    }

    info!("stophook success !!!");
}
